﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Reflection.Emit;

namespace CH.Study.ConsoleApp.TableBindToToEntityList
{
    public class TableBindToEntityListTest : ITest
    {
        public void Start()
        {
            Stopwatch t = new Stopwatch();
            t.Start();
            DataTable dt = TestData.Members;
            Console.WriteLine("提取测试数据:" + t.ElapsedMilliseconds + "毫秒");
            Console.WriteLine("总共测试数据:" + dt.Rows.Count + "条");
            t.Stop();



            t.Reset();
            t.Start();
            List<Member> list1 = DataTableBindToEntityListForDirect(dt);
            t.Stop();
            Console.WriteLine("直接绑定:" + t.ElapsedMilliseconds + "毫秒");











            t.Reset();
            t.Start();
            List<Member> list2 = DataTableBindToEntityListForReflection<Member>(dt);
            t.Stop();
            Console.WriteLine("反射绑定:" + t.ElapsedMilliseconds + "毫秒");









            t.Reset();
            t.Start();
            List<Member> list3 = DataTableBindToEntityListForExpression<Member>(dt);
            t.Stop();
            Console.WriteLine("表达式绑定:" + t.ElapsedMilliseconds + "毫秒");









            t.Reset();
            t.Start();
            List<Member> list4 = DataTableBindToEntityListForEmit<Member>(dt);
            t.Stop();
            Console.WriteLine("Emit绑定:" + t.ElapsedMilliseconds + "毫秒");
            t.Reset();




            Console.Read();

        }

        /// <summary>
        /// 直接绑定
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public List<Member> DataTableBindToEntityListForDirect(DataTable dt)
        {
            List<Member> list = new List<Member>();
            Member m = new Member();
            foreach (DataRow row in dt.Rows)
            {
                m.Age = row["Age"] == null ? 0 : Convert.ToInt32(row["Age"]);
                m.Level = row["Level"] == null ? 0 : Convert.ToInt32(row["Level"]);
                m.Name = row["Name"] == null ? "" : row["Name"].ToString();
                m.Email = row["Email"] == null ? "" : row["Email"].ToString();
                m.ID = Convert.ToInt32(row["ID"]);
                m.AddTime = Convert.ToDateTime(row["AddTime"]);
                list.Add(m);
            }
            return list;

        }

        /// <summary>
        /// 反射绑定
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public List<T> DataTableBindToEntityListForReflection<T>(DataTable dt)
        {
            List<T> list = new List<T>();
            Type type = typeof(T);
            foreach (DataRow row in dt.Rows)
            {

                T t = (T)Activator.CreateInstance(type);
                foreach (var item in type.GetProperties())
                {
                    item.SetValue(t, row[item.Name], null);
                }
                list.Add(t);
            }


            return list;
        }


        
        /// <summary>
        /// 表达式树绑定
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>
        public List<T> DataTableBindToEntityListForExpression<T>(DataTable dt)
        {
           
            Type type = typeof(Member);
            ParameterExpression p = Expression.Parameter(typeof(DataRow), "row");
            PropertyInfo[] propertyInfos = type.GetProperties();
            MemberBinding[] e = new MemberBinding[propertyInfos.Count()];
            for (int i = 0; i < propertyInfos.Count(); i++)
            {

                e[i] = Expression.Bind(propertyInfos[i], Expression.Convert(Expression.Call(p, typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(string) }), Expression.Constant(propertyInfos[i].Name)), propertyInfos[i].PropertyType));
            }

            NewExpression newExpression = Expression.New(type.GetConstructors().First());
            MemberInitExpression memberInitExpression = Expression.MemberInit(newExpression, e);
            LambdaExpression lambda = Expression.Lambda(memberInitExpression, p);
            Delegate de = lambda.Compile();
            

            List<T> list = new List<T>();
            foreach (DataRow row in dt.Rows)
            {
                T t = (T)de.DynamicInvoke(row);
                list.Add(t);
            }


            return list;
        }

        /// <summary>
        /// Emit绑定
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>
        delegate object EmitDelegate(DataRow row);
        public List<T> DataTableBindToEntityListForEmit<T>(DataTable dt)
        {

            MethodInfo getValueMethod = typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(int) });
            MethodInfo isDBNullMethod = typeof(DataRow).GetMethod("IsNull", new Type[] { typeof(int) });
         

            //生成一个方法
            DynamicMethod method = new DynamicMethod("BindToEntityListForEmit", typeof(T), new Type[] { typeof(DataRow) }, typeof(T), true);
            //开始构造方法的主体IL代码
            ILGenerator generator = method.GetILGenerator();
            //生成一个本地变量 result
            LocalBuilder result = generator.DeclareLocal(typeof(T));
            //实例化本地变量 result
            generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
            //设置堆栈中的值 到本地变量
            generator.Emit(OpCodes.Stloc, result);

            

            for (int i = 0; i < dt.Columns.Count; i++)
            {
                PropertyInfo pi = typeof(T).GetProperty(dt.Columns[i].ColumnName);
                //定义一个标记 这里是如果是null的时候跳转使用的标记
                Label endIfLabel = generator.DefineLabel();
                if (pi != null && pi.GetSetMethod() != null)
                {
                    //加载参数1 即DataRow  到堆栈
                    generator.Emit(OpCodes.Ldarg_0);
                    //加载整形 i 到堆栈
                    generator.Emit(OpCodes.Ldc_I4, i);
                    //调用方DataRow.IsNull(i) 并把返回值保存到堆栈中 真=1 假=0
                    generator.Emit(OpCodes.Callvirt, isDBNullMethod);
                    //判断堆栈里面的值是否为真 真=1 假=0 如果不是那么跳转到endIfLabel标记
                    generator.Emit(OpCodes.Brtrue, endIfLabel);
                    //加载本地变量result 到堆栈
                    generator.Emit(OpCodes.Ldloc, result);
                    //加载参数1 即DataRow  到堆栈
                    generator.Emit(OpCodes.Ldarg_0);
                    //加载整形 i 到堆栈
                    generator.Emit(OpCodes.Ldc_I4, i);
                    //调用方法DataRow.get_Item(i) 并把返回值保存到堆栈中
                    generator.Emit(OpCodes.Callvirt, getValueMethod);
                    //拆箱操作 堆栈里面的值
                    generator.Emit(OpCodes.Unbox_Any, pi.PropertyType);
                    //调用属性的Set方法 把堆栈中的值设置到属性中
                    generator.Emit(OpCodes.Callvirt, pi.GetSetMethod());
                    //null标记 如果数据为空那么程序将会直接跳转到这个标记这里
                    generator.MarkLabel(endIfLabel);
                }
            }

            //加载本地变量 到堆栈
            generator.Emit(OpCodes.Ldloc, result);
            generator.Emit(OpCodes.Ret);
            EmitDelegate de = (EmitDelegate)method.CreateDelegate(typeof(EmitDelegate));
            
    


            List<T> list = new List<T>();
            foreach (DataRow row in dt.Rows)
            {
                T t = (T)de(row);
                list.Add(t);
            }


            return list;
        }





    }
}
